//GA omp
//$Id: ga_omp.h 186 2009-09-13 18:00:24Z Oleg.Bulychov $
#pragma once
#ifndef GA_OMP_H
#define GA_OMP_H

#include "src/heo/include/easy_omp.h"
#include "ga_common.h"
//__________________________________________________________________________________
//__________________________________________________________________________________
/**
The Global Population Model
*/
template<class P, class S, class C>
class GA_OMP:
    public GA<P, S, C>
{
public:
    typedef P TProblem;
    typedef S TSolution;
    typedef C TConfig;
    typedef typename S::TDirection TDirection;
    typedef typename S::TCostType  TCostType;

    static void solve(TProblem const& problem, TSolution& sol, TConfig const& cfg)
    {
        uniform_random rnd(omp_get_thread_num());
        for (int N = 1; N <= cfg.run_count_; ++N)
        {
            GA_OMP<TProblem, TSolution, TConfig> ga;
            ga.init(problem, cfg);
            ga.generate_Random_Population(problem, cfg, rnd);
            {
#pragma omp parallel
                {
                    ga.evaluate_Parents_OMP(problem);
                }
            }
            for (int step = 1; step <= cfg.max_step_; ++step)
            {
                ga.select_Parents(cfg, rnd);

                //:reduce mutation level by time
                double m = cfg.mutation_probability_;
                if (cfg.mutation_fadeout_)
                    m *= (exp(exp(4. * log(1. - (step - 1) / cfg.max_step_))) - 1.);
                {
#pragma omp parallel
                    {
                        ga.crossover_OMP(cfg);
                        ga.mutation_OMP(m);
                        ga.evaluate_Offsprings_OMP(problem);
                    }
                }
                ga.select_Offsprings(cfg, rnd);
                ga.update_Solution(sol, cfg, step, 0);
                if (sol.is_Solved(problem))
                {
                    std::cout << "solved at step " << step << lf;
                    break;
                }
            }
        }
    }
private:
    using GA<P, S, C>::parent_;
    using GA<P, S, C>::parent_size_;
    using GA<P, S, C>::offspring_;
    using GA<P, S, C>::offspring_size_;

    void evaluate_Parents_OMP(TProblem const& problem)
    {
#pragma omp for
        for (int i = 0; i < parent_size_; ++i)
        {
            parent_[i]->evaluate(problem);
        }
    }

    void evaluate_Offsprings_OMP(TProblem const& problem)
    {
#pragma omp for
        for (int i = 0; i < offspring_size_; ++i)
        {
            offspring_[i]->evaluate(problem);
        }
    }

    void crossover_OMP(TConfig const& cfg)
    {
#pragma omp for
        for (int i = 0; i < offspring_size_ - 1; i += 2)
        {
            uniform_random rnd(omp_get_thread_num());
            if (rnd.next_Double() < cfg.crossover_probability_)
                offspring_[i]->cross(*offspring_[i + 1], rnd);
        }
    }

    void mutation_OMP(double mutationProbability)
    {
#pragma omp for
        for (int i = 0; i < offspring_size_; ++i)
        {
            uniform_random rnd(omp_get_thread_num());
            offspring_[i]->mutate(mutationProbability, rnd);
        }
    }

};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________

#endif
//EOF!
